package com.zmc.lostfound.post.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zmc.lostfound.common.exception.MyException;
import com.zmc.lostfound.common.feign.AuthFeign;
import com.zmc.lostfound.common.utils.Result;
import com.zmc.lostfound.common.utils.redis.RedisCacheUtil;
import com.zmc.lostfound.post.dao.CommentDao;
import com.zmc.lostfound.post.entity.CommentEntity;
import com.zmc.lostfound.post.service.CommentService;
import com.zmc.lostfound.post.vo.*;
import com.zmc.lostfound.post.vo.post.FromUser;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import static com.zmc.lostfound.common.constant.RedisConstant.COMMENT_LIKE_KEY_PREFIX;

/**
 * @author ZMC
 * @description 评论管理业务类
 * @email 2869581855@qq.com
 * @date 2023-07-05 15:49:44
 */
@Service
public class CommentServiceImpl extends ServiceImpl<CommentDao, CommentEntity> implements CommentService {

    @Resource
    private CommentService currentProxy;

    private final AuthFeign authFeign;

    private final RedisCacheUtil redisCacheUtil;

    private final ThreadPoolExecutor threadPoolExecutor;

    public CommentServiceImpl(AuthFeign authFeign, RedisCacheUtil redisCacheUtil, ThreadPoolExecutor threadPoolExecutor) {
        this.authFeign = authFeign;
        this.redisCacheUtil = redisCacheUtil;
        this.threadPoolExecutor = threadPoolExecutor;
    }

    /**
     * 用户发布评论
     *
     * @param commentPublishVO 用户发布评论请求体
     * @return Result
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Result publish(CommentPublishVO commentPublishVO) {
        CommentEntity commentEntity = BeanUtil.copyProperties(commentPublishVO, CommentEntity.class);
        // 如果父评论id为null，说明该评论是一级评论
        if (commentEntity.getParentId() == null) {
            commentEntity.setParentId(0L);
            commentEntity.setTreeLevel(1);
        }
        // 设置点赞量为0
        commentEntity.setLikeNumber(0);
        if (!this.save(commentEntity)) {
            MyException.throwException("评论发布失败");
        }
        return Result.ok("评论发布成功");
    }

    /**
     * 按主键id删除评论
     *
     * @param id 主键id
     * @return Result
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Result deleteById(Long id) {
        if (id == null || id <= 0) {
            MyException.throwException("删除失败: 评论id不能为空且必须大于0");
        }
        if (this.removeById(id)) {
            // 先查询出该评论的所有子评论ids
            List<Long> childIds = this.baseMapper.queryChildIdsByParentId(id);
            // 批量删除所有子评论
            if (CollectionUtil.isNotEmpty(childIds)) {
                this.removeByIds(childIds);
            }
        }
        return Result.ok("删除成功");
    }

    /**
     * 按帖子id删除评论
     *
     * @param postId 帖子id
     * @return Result
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Result deleteBatchByPostId(Long postId) {
        if (postId == null || postId <= 0) {
            MyException.throwException("删除失败: 帖子id不能为空且必须大于0");
        }
        this.remove(new QueryWrapper<CommentEntity>().eq("post_id", postId));
        return Result.ok("删除成功");
    }

    /**
     * 按主键ids批量删除评论信息
     *
     * @param ids 主键ids
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void deleteBatchByIds(List<Long> ids) {
        if (CollectionUtil.isNotEmpty(ids)) {
            this.removeByIds(ids);
        }
    }

    /**
     * 按主键id查询评论基本信息
     *
     * @param id 主键id
     * @return CommentEntity
     */
    @Override
    public CommentEntity queryInfoById(Long id) {
        if (id == null || id <= 0) {
            MyException.throwException("查询失败: 评论id不能为空且必须大于0");
        }
        return this.getById(id);
    }

    /**
     * 获取评论列表中的所有用户信息
     *
     * @param commentEntityList 评论列表
     * @return Map<Long, FromUser>
     */
    private Map<Long, FromUser> getFromUserMap(List<CommentEntity> commentEntityList) {
        // 获取用户ids
        List<Long> userIds = commentEntityList.stream()
                .map(CommentEntity::getUserId)
                .distinct()
                .collect(Collectors.toList());
        // 批量查询用户部分信息
        Result result = authFeign.queryPartInfoByIds(userIds);
        if (result == null || HttpStatus.HTTP_OK != result.getCode()) {
            MyException.throwException("查询失败: 无法获取用户信息");
        }
        List<FromUser> fromUserList = result.getBeanList("fromUserList", FromUser.class);
        // 转换成map返回
        return fromUserList.stream()
                .collect(Collectors.toMap(FromUser::getId, fromUser -> fromUser, (key1, key2) -> key2));
    }

    /**
     * 按帖子id查询所有评论
     *
     * @param postId           帖子id
     * @param isLikeNumberSort 是否按热度排序(默认按发评时间排序)
     * @return PageResult
     */
    @Override
    public List<CommentDetailInfoVO> queryInfoByPostId(Long postId, Boolean isLikeNumberSort) {
        if (postId == null || postId <= 0) {
            MyException.throwException("查询失败: 帖子id不能为空且必须大于0");
        }
        // 1.按帖子id查询所有评论信息
        List<CommentEntity> commentEntityList = this.query()
                .eq("post_id", postId)
                .orderByAsc("tree_level")
                .orderByDesc(BooleanUtil.isTrue(isLikeNumberSort) ? "like_number" : "id")
                .list();
        if (CollectionUtil.isEmpty(commentEntityList)) {
            return null;
        }
        // 1.1 转换成map备用
        Map<Long, CommentDetailInfoVO> commentDetailInfoMap = commentEntityList.stream()
                .map(commentEntity -> BeanUtil.copyProperties(commentEntity, CommentDetailInfoVO.class))
                .collect(Collectors.toMap(CommentDetailInfoVO::getId, commentDetailInfoVO -> commentDetailInfoVO, (key1, key2) -> key2));
        // 1.2 获取评论列表中的所有用户信息
        Map<Long, FromUser> fromUserMap = this.getFromUserMap(commentEntityList);
        // 1.3 最终返回的结果集
        List<CommentDetailInfoVO> commentDetailInfoVOList = new ArrayList<>();
        // 1.4 用于存储将要删除的评论id
        List<Long> deleteCommentIds = new ArrayList<>();
        // 2.封装到commentDetailInfoVOList
        for (CommentEntity commentEntity : commentEntityList) {
            // 2.1 对于一级评论(根节点)，直接加入commentDetailInfoVOList
            if (commentEntity.getTreeLevel().equals(1)) {
                CommentDetailInfoVO commentDetailInfoVO = commentDetailInfoMap.get(commentEntity.getId());
                commentDetailInfoVO.setFromUser(fromUserMap.get(commentEntity.getUserId()));
                commentDetailInfoVOList.add(commentDetailInfoVO);
                continue;
            }
            // 2.2 如果该子评论的父评论应该被删除，那么这个子评论也应该被删除
            if (deleteCommentIds.contains(commentEntity.getParentId())) {
                deleteCommentIds.add(commentEntity.getId());
                continue;
            }
            // 2.3 对于有父节点的子评论，获取它的父评论
            CommentDetailInfoVO parentComment = commentDetailInfoMap.get(commentEntity.getParentId());
            if (parentComment != null) {
                if (parentComment.getChildrenCommentList() == null) {
                    parentComment.setChildrenCommentList(new ArrayList<>());
                }
                // 开始往ChildrenCommentList属性中放子评论
                CommentDetailInfoVO childRenComment = commentDetailInfoMap.get(commentEntity.getId());
                childRenComment.setFromUser(fromUserMap.get(commentEntity.getUserId()));
                parentComment.getChildrenCommentList().add(childRenComment);
                continue;
            }
            // 2.4 对于没有父节点的评论，说明它的父评论被删除了，那么它也应该被删除
            deleteCommentIds.add(commentEntity.getId());
        }
        // 3.异步删除deleteCommentIds中的评论
        CompletableFuture.runAsync(() -> currentProxy.deleteBatchByIds(deleteCommentIds), threadPoolExecutor);
        return commentDetailInfoVOList;
    }

    /**
     * 按帖子id查询所有一级评论
     *
     * @param postId           帖子id
     * @param isLikeNumberSort 是否按热度排序(默认按发评时间排序)
     * @return List<CommentDetailInfoVO>
     */
    @Override
    public List<CommentDetailInfoVO> queryOneLevelByPostId(Long postId, Boolean isLikeNumberSort) {
        if (postId == null || postId <= 0) {
            MyException.throwException("查询失败: 帖子id不能为空且必须大于0");
        }
        // 1.按帖子id查询所有一级评论
        List<CommentEntity> commentEntityList = this.query()
                .eq("post_id", postId)
                .eq("tree_level", 1)
                .orderByDesc(BooleanUtil.isTrue(isLikeNumberSort) ? "like_number" : "id")
                .list();
        if (CollectionUtil.isEmpty(commentEntityList)) {
            return null;
        }
        // 2.获取评论列表中的所有用户信息
        Map<Long, FromUser> fromUserMap = getFromUserMap(commentEntityList);
        // 3.封装结果集并返回
        return commentEntityList.stream()
                .map(commentEntity -> {
                    CommentDetailInfoVO commentDetailInfoVO = BeanUtil.copyProperties(commentEntity, CommentDetailInfoVO.class);
                    commentDetailInfoVO.setFromUser(fromUserMap.get(commentEntity.getUserId()));
                    return commentDetailInfoVO;
                }).collect(Collectors.toList());
    }

    /**
     * 按父评论id查询所有子评论
     *
     * @param parentId         父评论id
     * @param isLikeNumberSort 是否按热度排序(默认按发评时间排序)
     * @return List<CommentDetailInfoVO>
     */
    @Override
    public List<CommentDetailInfoVO> queryChildByParentId(Long parentId, Boolean isLikeNumberSort) {
        if (parentId == null || parentId <= 0) {
            MyException.throwException("查询失败: 父评论id不能为空且必须大于0");
        }
        // 1.按父评论id查询所有子评论
        List<CommentEntity> commentEntityList = this.baseMapper.queryChildByParentId(parentId, BooleanUtil.isTrue(isLikeNumberSort) ? "like_number" : "id");
        if (CollectionUtil.isEmpty(commentEntityList)) {
            return null;
        }
        // 1.1 转换成map备用
        Map<Long, CommentDetailInfoVO> commentDetailInfoMap = commentEntityList.stream()
                .map(commentEntity -> BeanUtil.copyProperties(commentEntity, CommentDetailInfoVO.class))
                .collect(Collectors.toMap(CommentDetailInfoVO::getId, commentDetailInfoVO -> commentDetailInfoVO, (key1, key2) -> key2));
        // 1.2 获取评论列表中的所有用户信息
        Map<Long, FromUser> fromUserMap = this.getFromUserMap(commentEntityList);
        // 1.3 最终返回的结果集
        List<CommentDetailInfoVO> commentDetailInfoVOList = new ArrayList<>();
        // 2.封装到commentDetailInfoVOList
        for (CommentEntity commentEntity : commentEntityList) {
            // 2.1 对于紧挨着父评论的子评论，直接加入commentDetailInfoVOList
            if (commentEntity.getParentId().equals(parentId)) {
                CommentDetailInfoVO commentDetailInfoVO = commentDetailInfoMap.get(commentEntity.getId());
                commentDetailInfoVO.setFromUser(fromUserMap.get(commentEntity.getUserId()));
                commentDetailInfoVOList.add(commentDetailInfoVO);
                continue;
            }
            // 2.2 对于有父节点的子评论，获取它的父评论
            CommentDetailInfoVO parentComment = commentDetailInfoMap.get(commentEntity.getParentId());
            if (parentComment != null) {
                if (parentComment.getChildrenCommentList() == null) {
                    parentComment.setChildrenCommentList(new ArrayList<>());
                }
                // 开始往ChildrenCommentList属性中放子评论
                CommentDetailInfoVO childRenComment = commentDetailInfoMap.get(commentEntity.getId());
                childRenComment.setFromUser(fromUserMap.get(commentEntity.getUserId()));
                parentComment.getChildrenCommentList().add(childRenComment);
            }
        }
        return commentDetailInfoVOList;
    }

    /**
     * 按父评论id查询所有扁平化子评论
     *
     * @param parentId         父亲id
     * @param isLikeNumberSort 是否按热度排序(默认按发评时间排序)
     * @return List<FlatChildCommentVO>
     */
    @Override
    public List<FlatChildCommentVO> queryFlatChildByParentId(Long parentId, Boolean isLikeNumberSort) {
        if (parentId == null || parentId <= 0) {
            MyException.throwException("查询失败: 父评论id不能为空且必须大于0");
        }
        // 1.按父评论id查询所有子评论
        List<CommentEntity> commentEntityList = this.baseMapper.queryChildByParentId(parentId, BooleanUtil.isTrue(isLikeNumberSort) ? "like_number" : "id");
        if (CollectionUtil.isEmpty(commentEntityList)) {
            return null;
        }
        // 1.1 转换成map备用
        Map<Long, CommentEntity> commentEntityMap = commentEntityList.stream()
                .collect(Collectors.toMap(CommentEntity::getId, commentEntity -> commentEntity, (key1, key2) -> key2));
        // 1.2 获取评论列表中的所有用户信息
        Map<Long, FromUser> fromUserMap = this.getFromUserMap(commentEntityList);
        // 1.3 最终返回的结果集
        List<FlatChildCommentVO> flatChildCommentVOList = new ArrayList<>(commentEntityList.size());
        // 2.封装到flatChildCommentVOList
        for (CommentEntity commentEntity : commentEntityList) {
            // 2.1封装评论的发布用户信息
            FlatChildCommentVO flatChildCommentVO = BeanUtil.copyProperties(commentEntity, FlatChildCommentVO.class);
            flatChildCommentVO.setFromUser(fromUserMap.get(commentEntity.getUserId()));
            // 2.2封装三级与三级以下评论的父评论的发布用户信息
            if (commentEntity.getTreeLevel() > 2) {
                // 获取父评论
                CommentEntity parentComment = commentEntityMap.get(commentEntity.getParentId());
                // 设置父评论的发布用户信息
                flatChildCommentVO.setParentCommentUser(fromUserMap.get(parentComment.getUserId()));
            }
            flatChildCommentVOList.add(flatChildCommentVO);
        }
        return flatChildCommentVOList;
    }

    /**
     * 按用户id查询评论的回复消息
     *
     * @param userId 用户id
     * @return List<ReplyCommentVO>
     */
    @Override
    public List<ReplyCommentVO> queryReplyByUserId(Long userId) {
        if (userId == null || userId <= 0) {
            MyException.throwException("查询失败: 用户id不能为空且必须大于0");
        }
        // 1.按用户id查询自己发布的所有评论ids
        List<CommentEntity> myCommentList = this.query().select("id").eq("user_id", userId).list();
        if (CollectionUtil.isEmpty(myCommentList)) {
            return null;
        }
        List<Long> myCommentIds = myCommentList.stream().map(CommentEntity::getId).collect(Collectors.toList());
        // 2.查询父评论id在myCommentIds中的评论信息(回复我的评论都是我的评论的子评论)
        List<CommentEntity> childCommentList = this.query()
                .in("parent_id", myCommentIds)
                .orderByDesc("id").list();
        if (CollectionUtil.isEmpty(childCommentList)) {
            return null;
        }
        // 3.获取评论列表中的所有用户信息
        Map<Long, FromUser> fromUserMap = this.getFromUserMap(childCommentList);
        // 4.封装成List<ReplyCommentVO>返回
        return childCommentList.stream()
                .map(childComment -> {
                    ReplyCommentVO replyCommentVO = BeanUtil.copyProperties(childComment, ReplyCommentVO.class);
                    // 设置每个子评论的发评用户信息
                    replyCommentVO.setChildCommentUser(fromUserMap.get(childComment.getUserId()));
                    return replyCommentVO;
                }).collect(Collectors.toList());
    }

    /**
     * 用户点赞评论
     *
     * @param commentLikeVO 用户点赞评论请求体
     * @return Result
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Result like(CommentLikeVO commentLikeVO) {
        Long userId = commentLikeVO.getUserId();
        Long commentId = commentLikeVO.getCommentId();
        // 1.Set集合的Key，该set集合用于存储点赞过该评论的用户id
        String key = COMMENT_LIKE_KEY_PREFIX + commentId;
        // 2.该用户未点过赞
        if (BooleanUtil.isFalse(redisCacheUtil.isMemberSet(key, userId.toString()))) {
            // 2.1点赞数量+1
            if (!this.update()
                    .setSql("like_number = like_number + 1")
                    .eq("id", commentId)
                    .update()
            ) {
                MyException.throwException("点赞失败");
            }
            // 2.2将点赞用户id保存到redis的set集合中
            redisCacheUtil.addSet(key, userId.toString());
        } else {
            // 3.该用户已点过赞
            // 3.1点赞数量-1
            if (!this.update()
                    .setSql("like_number = like_number - 1")
                    .eq("id", commentId)
                    .update()
            ) {
                MyException.throwException("取消点赞失败");
            }
            // 3.2将点赞用户id从redis的set集合中移除
            redisCacheUtil.removeSet(key, userId.toString());
        }
        // 4.查询评论的当前点赞量
        CommentEntity commentInfo = this.query()
                .select("id", "like_number")
                .eq("id", commentId).one();
        return Result.ok().put("commentInfo", commentInfo);
    }

}